package com.sprouting.personal.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sprouting.personal.annotations.ApiIdempotent;
import com.sprouting.personal.interfaces.common.Constant;
import com.sprouting.personal.interfaces.common.ConstantPath;
import com.sprouting.personal.model.common.ResultJson;
import com.sprouting.personal.model.common.jsr.AddGroup;
import com.sprouting.personal.model.common.jsr.UpdGroup;
import com.sprouting.personal.model.entity.Book;
import com.sprouting.personal.model.entity.Config;
import com.sprouting.personal.model.entity.Dict;
import com.sprouting.personal.model.vo.BookStatistics;
import com.sprouting.personal.model.vo.HomeworkSubmitStatistics;
import com.sprouting.personal.service.BookService;
import com.sprouting.personal.service.ConfigService;
import com.sprouting.personal.service.DictService;
import com.sprouting.personal.utils.CheckTool;
import com.sprouting.personal.utils.EntityTool;
import com.sprouting.personal.utils.IdTool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 书籍
 *
 * @author ：LX（长沙麓谷）
 * @date 创建时间： 2022/8/12 10:33
 */
@RequestMapping("/book")
@RestController
@Slf4j
public class BookController {

    @Autowired
    private BookService bookService;
    @Autowired
    private DictService dictService;
    @Autowired
    private ConfigService configService;

    /**
     * 文件存储基础路径
     */
    @Value("${upload.data.base.path}")
    private String basePath;
    /**
     * 上传图片类型
     */
    @Value("#{'${upload.image.type}'.split(',')}")
    private List<String> uploadImageTypeList;
    /**
     * 图片文件上传最大长度
     */
    @Value("${upload.image.max.length}")
    private Integer uploadImageMaxLength;





    // ================== 统计 start =============


    /**
     * 获取最喜欢的作家排行
     * @return
     */
    @GetMapping("getFavoriteWriters")
    public ResultJson getFavoriteWriters(){
        return ResultJson.success(bookService.getFavoriteWriters(StpUtil.getLoginIdAsString(), 10));
    }

    /**
     * 获取不同类型书籍数量
     * @return
     */
    @GetMapping("getBookTypeNum")
    public ResultJson getBookTypeNum(){
        return ResultJson.success(bookService.getBookTypeNum(StpUtil.getLoginIdAsString()));
    }

    /**
     * 获取评分区间书籍的数量
     * @return
     */
    @GetMapping("getRatingRange")
    public ResultJson getRatingRange(){
        return ResultJson.success(bookService.getRatingRange(StpUtil.getLoginIdAsString()));
    }

    /**
     * 获取过去N个月每个月的书籍阅读量
     * @return
     */
    @GetMapping("getBookReadingVolume")
    public ResultJson getBookReadingVolume(){
        List<BookStatistics> bookStatisticsList = bookService.getBookReadingVolume(StpUtil.getLoginIdAsString());
        if (CollUtil.isEmpty(bookStatisticsList)){
            bookStatisticsList = new ArrayList<>();
        }
        Map<String, BookStatistics> map = bookStatisticsList.stream().collect(Collectors.toMap(BookStatistics::getMonth, Function.identity()));

        // 36个月
        DateTime offset = DateUtil.offset(new Date(), DateField.MONTH, -36);
        List<DateTime> list = DateUtil.rangeToList(offset, new Date(), DateField.MONTH);
        for (DateTime dateTime : list) {
            String date = DateUtil.format(dateTime, "yyyy-MM");
            BookStatistics bookStatistics = map.get(date);
            if (bookStatistics == null){
                bookStatistics = new BookStatistics();
                bookStatistics.setMonth(date);
                bookStatistics.setNum(0L);
                bookStatisticsList.add(bookStatistics);
            }
        }

        // 排序
        List<BookStatistics> sortedList = bookStatisticsList.stream().sorted(Comparator.comparing(BookStatistics::getMonth)).collect(Collectors.toList());
        return ResultJson.success(sortedList);
    }

    // ================== 统计 end =============

    /**
     * 根据书籍ID获取书籍信息
     * @param id 书籍ID
     * @return
     */
    @PostMapping("/getBookById")
    public ResultJson<Book> getBookById(String id){
        if (CheckTool.checkNull(id)){
            return ResultJson.errorIllegal();
        }

        return ResultJson.success(bookService.getBookById(id));
    }

    /**
     * 分页查询书籍信息
     * @param page 分页
     * @param book 查询参数
     * @return
     */
    @PostMapping("/getBookListPage")
    public ResultJson getBookListPage(Page page, Book book){
        if (book == null){
            book = new Book();
        }
        book.setUserId(StpUtil.getLoginIdAsString());

        return ResultJson.success(bookService.getBookListPage(page, book));
    }

    /**
     * 物理删除书籍
     * @param id 书籍ID
     * @return
     */
    @PostMapping("/delBook")
    public ResultJson delBook(String id){
        if (CheckTool.checkNull(id)){
            return ResultJson.errorIllegal();
        }
        Book book = bookService.getBookById(id);
        if (book == null){
            return ResultJson.errorPram();
        }

        // 直接物理删除书籍
        bookService.removeById(id);

        String icoPath = book.getIcoPath();
        if (!CheckTool.checkNull(icoPath)){
            // 做容错措施，如果是目录，不进行操作，数据非常重要，一定要多检查
            if (FileUtil.isFile(basePath + icoPath)){
                FileUtil.del(basePath + icoPath);
            } else {
                log.error("要删除的是数据是目录，跳过该删除操作：{}", basePath + icoPath);
            }
        }

        String bookPath = book.getBookPath();
        if (!CheckTool.checkNull(bookPath)){
            // 做容错措施，如果是目录，不进行操作，数据非常重要，一定要多检查
            if (FileUtil.isFile(basePath + bookPath)){
                FileUtil.del(basePath + bookPath);
            } else {
                log.error("要删除的是数据是目录，跳过该删除操作：{}", basePath + bookPath);
            }
        }

        return ResultJson.success(null);
    }

    /**
     * 保存书籍
     * @param book 书本
     * @param file 上传的书本封面
     * @param bookFile 上传的书本文件
     * @return
     */
    @PostMapping("/saveBook")
    @ApiIdempotent
    public ResultJson<Book> saveBook(@Validated(AddGroup.class) Book book, @RequestParam(value = "file", required = false) MultipartFile file, @RequestParam(value = "bookFile", required = false) MultipartFile bookFile){
        if (CheckTool.checkNull(basePath)){
            return ResultJson.error("图片存储路径未初始化，请补充application.properties配置信息");
        }
        String msg = checkBook(book);
        if (msg != null){
            return ResultJson.error(msg);
        }

        // 上传封面
        if (file != null){
            String err = saveBookCover(file, book);
            if (err != null){
                return ResultJson.error(err);
            }
        }
        // 上传书本
        if (bookFile != null){
            String err = saveBook(bookFile, book);
            if (err != null){
                return ResultJson.error(err);
            }
        }

        book.setId(IdTool.getId());
        book.setUserId(StpUtil.getLoginIdAsString());
        EntityTool.buildSaveBase(book, StpUtil.getLoginIdAsString());
        bookService.save(book);
        return ResultJson.success(book);
    }

    /**
     * 更新书籍上传文件
     * @param book 书本
     * @param file 上传的书本封面
     * @param bookFile 上传的书本文件
     * @return
     */
    @PostMapping("/updBook")
    @ApiIdempotent
    public ResultJson<Book> updBook(@Validated(UpdGroup.class) Book book, @RequestParam(value = "file", required = false) MultipartFile file, @RequestParam(value = "bookFile", required = false) MultipartFile bookFile){
        String msg = checkBook(book);
        if (msg != null){
            return ResultJson.error(msg);
        }
        // 先找到旧的书籍信息，防止数据被覆盖
        Book bookDb = bookService.getBookById(book.getId());
        if (bookDb == null){
            return ResultJson.errorPram();
        }

        // 上传封面
        if (file != null){
            String err = saveBookCover(file, book);
            if (err != null){
                return ResultJson.error(err);
            }
        }
        // 上传书本
        if (bookFile != null){
            String err = saveBook(bookFile, book);
            if (err != null){
                return ResultJson.error(err);
            }
        }

        EntityTool.buildUpdBase(book, StpUtil.getLoginIdAsString());
        bookService.updateById(book);

        // 清理原始数据
        clearOldData(file, bookFile, bookDb);

        return ResultJson.success(book);
    }

    /**
     * 获取书籍类型
     * @return
     */
    @PostMapping("/getBookType")
    public ResultJson<List<Dict>> getBookType(){
        List<Dict> dictList = dictService.getType(Constant.RetainDictGroup.BOOK_GROUP, StpUtil.getLoginIdAsString());
        return ResultJson.success(dictList);
    }

    /**
     * 获取阅读书籍的地址
     * @return
     */
    @PostMapping("/getBookLocale")
    @ApiIdempotent
    public ResultJson<Config> getBookLocale(){
        Config config = configService.getConfigByCodeAutomaticCompletion(Constant.ConfigCode.READING_ADDRESS, StpUtil.getLoginIdAsString());
        return ResultJson.success(config);
    }

    /**
     * 保存书本封面
     * @param file 上传的封面文件
     * @param book 书本信息
     * @return 非null返回，则是有错误信息
     */
    private String saveBookCover(MultipartFile file, Book book){
        String originalFilename = file.getOriginalFilename();
        if (CheckTool.checkNull(originalFilename)){
            return "获取文件名失败";
        }
        String suffix = FileUtil.getSuffix(originalFilename).toLowerCase();
        if (CheckTool.checkNull(suffix)){
            // 检查特殊情况下的文件名不符合
            return "非法上传图片名，不支持的后缀";
        }
        if (uploadImageTypeList != null){
            if (!uploadImageTypeList.contains(suffix)){
                return "上传图片类型不支持";
            }
        }
        if (file.getSize() > uploadImageMaxLength){
            return "上传图片超过限定大小";
        }

        String path = String.format(ConstantPath.Path.BOOK_ICO, basePath, StpUtil.getLoginIdAsString(), IdTool.getId(), suffix);
        File toFile = new File(path);
        // 上传路径
        if (!FileUtil.exist(toFile)){
            FileUtil.touch(toFile);
        }

        try {
            file.transferTo(toFile);
        } catch (IOException e) {
            log.error("图片上传失败", e);
            return "图片上传失败";
        }

        // 只保存相对路径
        String replacePath = path.replace(basePath, "");
        book.setIcoPath(replacePath);
        return null;
    }

    /**
     * 保存书本
     * @param file 上传的书本文件
     * @param book 书本信息
     * @return 非null返回，则是有错误信息
     */
    private String saveBook(MultipartFile file, Book book){
        String originalFilename = file.getOriginalFilename();
        if (CheckTool.checkNull(originalFilename)){
            return "获取文件名失败";
        }
        String suffix = FileUtil.getSuffix(originalFilename).toLowerCase();
        if (CheckTool.checkNull(suffix)){
            // 检查特殊情况下的文件名不符合
            return "非法上传文件名";
        }

        String path = String.format(ConstantPath.Path.BOOK_FILE, basePath, StpUtil.getLoginIdAsString(), IdTool.getId(), suffix);
        File toFile = new File(path);
        // 上传路径
        if (!FileUtil.exist(toFile)){
            FileUtil.touch(toFile);
        }

        try {
            file.transferTo(toFile);
        } catch (IOException e) {
            log.error("书本上传失败", e);
            return "书本上传失败";
        }

        // 只保存相对路径
        String replacePath = path.replace(basePath, "");
        book.setBookPath(replacePath);
        return null;
    }

    /**
     * 清理旧的书籍文件
     * @param file 上传的图片文件
     * @param bookFile 上传的书籍文件
     * @param bookDb 书籍信息
     */
    private void clearOldData(MultipartFile file, MultipartFile bookFile, Book bookDb){
        // 只有新增之后才能删除旧的，否则不去操作
        if (file != null){
            String icoPath = bookDb.getIcoPath();
            if (!CheckTool.checkNull(icoPath)){
                // 做容错措施，如果是目录，不进行操作，数据非常重要，一定要多检查
                if (FileUtil.isFile(basePath + icoPath)){
                    FileUtil.del(basePath + icoPath);
                } else {
                    log.error("要删除的是数据是目录，跳过该删除操作：{}", basePath + icoPath);
                }
            }
        }
        if (bookFile != null){
            String bookPath = bookDb.getBookPath();
            if (!CheckTool.checkNull(bookPath)){
                // 做容错措施，如果是目录，不进行操作，数据非常重要，一定要多检查
                if (FileUtil.isFile(basePath + bookPath)){
                    FileUtil.del(basePath + bookPath);
                } else {
                    log.error("要删除的是数据是目录，跳过该删除操作：{}", basePath + bookPath);
                }
            }
        }
    }

    /**
     * 校验书籍
     * @param book 书籍信息
     * @return 非null则是错误信息
     */
    private String checkBook(Book book){
        // 校验重复书籍
        Book bookDb = bookService.getBookRepeatQuery(book.getId(), StpUtil.getLoginIdAsString(), book.getName(), book.getAuthor());
        if (bookDb != null){
            return "该书本已入库，不需要重复录入";
        }

        return null;
    }
}
